//!
//! Copyright (c) 2024-2999 北京心月狐科技有限公司 All rights reserved.
//!
//! https://www.mxxshop.com
//!
//! Licensed 并不是自由软件，未经许可不能去掉 MxxShop 相关版权
//!
//! 版权所有，侵权必究！
//!

use std::future::{ready, Ready};
use actix_http::body::EitherBody;
use actix_session::SessionExt;
use actix_web::{
    dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform},
    Error, HttpResponse,
};
use actix_web::http::header;
use futures_util::{
    future::{LocalBoxFuture},
};
use regex::Regex;
use crate::core::constant::user_const::SESSION_USER_KEY;
use crate::utils::settings::Settings;

#[derive(Clone)]
pub struct AuthService;

impl<S, B> Transform<S, ServiceRequest> for AuthService
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<EitherBody<B>>;
    type Error = Error;
    type InitError = ();
    type Transform = AuthMiddleware<S>;
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        ready(Ok(AuthMiddleware { service }))
    }
}

pub struct AuthMiddleware<S> {
    service: S,
}

impl<S, B> Service<ServiceRequest> for AuthMiddleware<S>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<EitherBody<B>>;
    type Error = Error;
    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;

    forward_ready!(service);

    fn call(&self, req: ServiceRequest) -> Self::Future {
        // 进行鉴权操作，判断是否有权限
        if has_permission(&req) {
            
            // 有权限，继续执行后续中间件
            let res = self.service.call(req);
            Box::pin(async move { res.await.map(ServiceResponse::map_into_left_body) })
        } else {
            // 没有权限，立即返回响应
            let (request, _pl) = req.into_parts();
            let response = HttpResponse::Found()
                .insert_header((header::LOCATION, "/user/login"))
                .finish()
                .map_into_right_body();

            Box::pin(async { Ok(ServiceResponse::new(request, response)) })
        }
    }
}

fn has_permission(req: &ServiceRequest) -> bool {
    let token: Option<String> = req.get_session().get(SESSION_USER_KEY).unwrap_or(None);
    let has_valid_token = token.unwrap_or_default().len() > 0;
    let setting = Settings::get();
    let is_path_valid = contains_path(setting.server.auth_excludes.as_str(), req.path(), setting.server.auth_url.as_str());
    has_valid_token || is_path_valid
}

/// 检查用逗号分隔的路径列表中是否包含与通配符路径匹配的路径。
///
/// # Arguments
///
/// * `csv_string` - 用逗号分隔的路径字符串，例如 "/user/login,/user/logout,/user/forgot,/user/supplier-join,/admin/*"
/// * `query_path` - 要查找的路径
/// * `blocked_path` - 需要阻止的路径
/// # Returns
///
/// * `true` 如果路径在列表中（包括匹配通配符的路径）否则 `false`
pub fn contains_path(csv_string: &str, query_path: &str, blocked_path: &str) -> bool {
    let paths: Vec<&str> = csv_string.split(',')
        .map(|s| s.trim())
        .filter(|s| !s.is_empty())
        .collect();

    // 如果查询路径匹配阻止路径且不在放行的路径中，则返回 false
    if matches_blocked_path(query_path, blocked_path) && !is_allowed_path(query_path, &paths) {
        return false;
    }

    paths.iter().any(|&path| {
        if path.contains("{*}") {
            let regex_pattern = path.replace("{*}", ".*");
            if let Ok(regex) = Regex::new(&regex_pattern) {
                regex.is_match(query_path)
            } else {
                false
            }
        } else if path.ends_with("/*") {
            let prefix = &path[..path.len() - 2];
            query_path.starts_with(prefix) && query_path.len() > prefix.len()
        } else {
            path == query_path
        }
    })
}

// 检查路径是否匹配阻止的路径
fn matches_blocked_path(query_path: &str, blocked_path: &str) -> bool {
    if blocked_path.ends_with("/*") {
        let prefix = &blocked_path[..blocked_path.len() - 2];
        return query_path.starts_with(prefix);
    } else {
        return blocked_path == query_path;
    }
}

// 检查查询路径是否在允许的路径列表中
fn is_allowed_path(query_path: &str, paths: &[&str]) -> bool {
    paths.contains(&query_path)
}

